import configs from '../config';
import Websocket from 'ws'
import Asynclock = require('async-lock');

import mystatus from '../define/status';
import cubeMsg from '../define/cubeMessage';

const lock = new Asynclock({ timeout: 5000 });

export default class CUBECONN {
    private cubeMessage: cubeMsg;
    private static instance: CUBECONN;
    private wsock: Websocket;
    private isConnected: boolean = false;

    constructor() {
        this.cubeMessage = {
            HEAD: {
                tag: "MESG",
                version: 65537,
                record_type: '',
                record_subtype: '',
                flow: 0,
                record_num: 0,
                expand_num: 0,
                nonce: ''
            },
            RECORD: [],
            EXPAND: []
        }
    }

    /**
     * public:
     */

    /**
     * 懒汉式，获取实例
     * @returns CUBECONN实例对象
     */
    public static getinstance(): CUBECONN {
        console.log('懒汉式，获取实例...');
        if (!this.instance) {
            this.instance = new CUBECONN();
        }
        return CUBECONN.instance;
    }



    public recvMessage(type: string, subtype: string, record: any = null, expand: any = null): Promise<any> {
        let message = null;
        return new Promise(async (resolve, reject) => {
            lock.acquire("msg", async (done) => {
                if (!this.isConnected) {
                    await this.wsConnect();
                }
                try {
                    this.sendMessage(type, subtype, record, expand);
                    this.wsock.onmessage = (e) => {
                        message = null;
                        message = e.data;
                        console.log(`此时应该已从cube获得消息，其值为：${message}`);

                        if (!message) {
                            console.log('没有接收到任何消息');
                            resolve(mystatus.code.ERROR);
                            done();
                        }
                        resolve(message);
                    }
                } catch (error) {
                    console.log('接收数据出现错误' + error);
                    resolve(mystatus.code.ERROR);
                    done();
                } finally {
                    setTimeout(() => {
                        done();
                    }, 5000);
                }
            });

        })

    }

    public async sendMessage(type: string, subtype: string, record: any = null, expand: any = null) : Promise<boolean> {
        if (!this.isConnected) {
            await this.wsConnect();
        }
        try {
            if (record != null) {
                this.addRecord(record);
            }
            if (expand != null) {
                this.addExpand(expand)
            }
            this.addTypeSubtype(type, subtype);
            this.wsock.send(this.output());
            console.log('成功发送本条消息给cube！');
            return true;
        } catch (error) {
            console.log('Message send error:' + error);
            return false;
        }
    }



    /**
     * private:
     */
    private wsConnect() {
        return new Promise((resolve) => {
            if (!this.wsock) {
                this.wsock = new Websocket(`ws://${configs.cubeServer.host}:${configs.cubeServer.port}`);
            }
            this.wsock.onopen = (e) => {
                console.log('opened...');
                this.isConnected = true;
                resolve(0);
            }
            this.wsock.onclose = (e) => {
                console.log('websocket closed');
                this.isConnected = false;
                resolve(1);
            }
            this.wsock.onerror = (e) => {
                console.log('websocket error');
                this.isConnected = false;
                resolve(1);
            }
        });


    }

    private addTypeSubtype(type: string, subtype: string) {
        this.cubeMessage.HEAD.record_type = type;
        this.cubeMessage.HEAD.record_subtype = subtype;
    }

    private addRecord(recordInfo: any): number {
        this.cubeMessage.RECORD[this.cubeMessage.HEAD.record_num] = recordInfo;
        this.cubeMessage.HEAD.record_num++;
        return this.cubeMessage.HEAD.record_num;
    }

    private addExpand(expandInfo: any): number {
        this.cubeMessage.EXPAND[this.cubeMessage.HEAD.expand_num] = expandInfo;
        this.cubeMessage.HEAD.expand_num++;
        return this.cubeMessage.HEAD.expand_num;
    }

    private output(): any {
        return JSON.stringify(this.cubeMessage) + '\0';
    }
}